home *** CD-ROM | disk | FTP | other *** search
- /******************************************************************/
- /* man.c - a simple-minded man program for MiNT and TOS */
- /* Copyright (c) 1992 by HPP Biersma (schuller@dutiag.tudelft.nl) */
- /* This program comes under the GNU public license - */
- /* see the file "copying" for details. */
- /******************************************************************/
- /* bugs: - not UN*X compatible, either in source-code or behavior */
- /* - string operations are not safe, ie not limits checked */
- /******************************************************************/
- /* version: 0.4 (fourth released version), December 15, 1992 */
- /* written for: - GCC version 2.3.1, patchlevel 1 */
- /* - mintlib patchlevel 25 */
- /* compile with: gcc -o man.ttp man.c -mbaserel -mpcrel -O2 */
- /******************************************************************/
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <stat.h>
- #include <dirent.h> /* used for opendir(), readdir(), closedir() */
- #include <mintbind.h>
-
- /* Comment out the following definitions if you want subprograms */
- /* (nroff, less and cat) to get UN*X (not GEMDOS-like) filenames. */
- #define SUBPROGRAMS_GET_GEMDOS_NAMES /* the longest #define yet ? */
-
- /* Define a enumerated type used to indicated mode of operation */
- typedef enum {MODE_UNKNOWN, MODE_MAN, MODE_MAN_F, MODE_MAN_K} mode_type;
-
- /* Maximum length of path + file name */
- #define NAME_LENGTH 256
-
- /* prototypes for local (static) functions */
- static void volatile option_error(const int error);
- static int search_man_path(const char *section, const char *title);
- static int search_man_dir(const char *section, const char *title,
- const char *dir_name);
- static int search_section_specific(const char *section, const char *title,
- const char *dir_name);
- static int search_section_general(const char section_code, const char *title,
- const char *dir_name);
- static int find_best_file(const char *dir_name, const char *title,
- const char section_code,
- char *result_basename, char *result_fullname);
- static void display_file(const char *filename);
- static void format_file(const char *mandir_name, const char *manfile_name,
- const char *catfile_name);
- static int check_directory_name(const char *dirname);
- static void build_catdir_name(const char *dirname, const char section_code,
- char *catdir_name);
- static void build_mandir_name(const char *dirname, const char section_code,
- char *mandir_name);
- static int handle_whatis(const char *name, const mode_type mode);
- static void strip_filename(const char *name, char *stripped_name);
- static int print_apropos(const char *mandir, const char *name);
-
- /* Extern and local variables */
- static char man_path[NAME_LENGTH] = "/usr/man";
- static char pager[NAME_LENGTH] = "less -r -e";
- static char macro_package[NAME_LENGTH] = "an";
- static int use_pager = 1; /* Use a pager unless '-' option or not a tty */
-
-
- void main(int argc, char *argv[])
- {
- char *section = NULL, *ptr;
- mode_type use_mode = MODE_UNKNOWN;
- int real_parameter_found = 0;
-
- /* Set the search path for the man pages*/
- if ((ptr = getenv("MANPATH")) != NULL)
- strncpy(man_path, ptr, NAME_LENGTH - 1);
-
- /* Set the name of the pager used to show the man pages */
- if ((ptr = getenv("PAGER")) != NULL)
- strncpy(pager, ptr, NAME_LENGTH - 1);
-
- /* We are very forgiving in reading the command-line; if an option is */
- /* given several times, the last one given counts at the time it is */
- /* used. Also, several section names may be given without a title in */
- /* between. We only give an error message if no real parameter is */
- /* given, when an unknown or incomplete option is given, or when */
- /* options of different modes of operation are combined. Options that */
- /* take a parameter may have a space between the option and the value.*/
- argv += 1;
- ptr = argv[0];
- while (argc > 1)
- {
- if (*ptr == '-')
- {
- switch(ptr[1])
- {
- case 0x00:
- /* Option '-' given: do not use a pager; use `cat' instead */
- if ((use_mode == MODE_MAN_F) || (use_mode == MODE_MAN_K))
- option_error(1); /* doesn't return */
- use_mode = MODE_MAN;
- use_pager = 0;
- break;
- case 'M':
- /* Option '-M' given: alternative manual directory search path */
- if (ptr[2] != 0x00)
- strcpy(man_path, &(ptr[2]));
- else
- {
- /* space used after '-M') */
- if (argc == 2)
- {
- fprintf(stderr, "man: -M option needs an argument\n");
- exit(1);
- }
- else
- {
- argv += 1;
- strcpy(man_path, argv[0]);
- argc -= 1;
- }
- } /* End of else (space after -M option) */
- break;
- case 'T':
- /* Option '-T' given: alternative macro package to be used by nroff */
- if ((use_mode == MODE_MAN_F) || (use_mode == MODE_MAN_K))
- option_error(1); /* doesn't return */
- use_mode = MODE_MAN;
- if (ptr[2] != 0x00)
- strcpy(macro_package, &(ptr[2]));
- else
- {
- /* space used after '-T') */
- if (argc == 2)
- {
- fprintf(stderr, "man: -T option needs an argument\n");
- exit(1);
- }
- else
- {
- argv += 1;
- strcpy(macro_package, argv[0]);
- argc -= 1;
- }
- } /* End of else (space after -T option) */
- break;
- case 'k':
- if ((use_mode == MODE_MAN) || (use_mode == MODE_MAN_F))
- option_error(1); /* doesn't return */
- use_mode = MODE_MAN_K;
- break;
- case 'f':
- if ((use_mode == MODE_MAN) || (use_mode == MODE_MAN_K))
- option_error(1); /* doesn't return */
- use_mode = MODE_MAN_F;
- break;
- default:
- fprintf(stderr, "man: no option %s supported\n", ptr);
- option_error(0); /* doesn't return */
- } /* End of switch (letter after `-') */
- } /* End of if (option) */
- else if ((strlen(ptr) < 3) && (*ptr >= '1') && (*ptr <= '8') &&
- (use_mode != MODE_MAN_F) && (use_mode != MODE_MAN_K))
- {
- /* A section has been found */
- section = ptr;
- use_mode = MODE_MAN;
- }
- else
- {
- /* A real parameter has been found */
- real_parameter_found = 1;
- switch(use_mode)
- {
- case MODE_UNKNOWN:
- use_mode = MODE_MAN;
- case MODE_MAN:
- if (search_man_path(section, ptr) == 0)
- {
- if (section == NULL)
- fprintf(stderr, "man: %s not found\n", ptr);
- else
- fprintf(stderr, "man: %s (%s) not found\n", ptr, section);
- }
- break;
- case MODE_MAN_F:
- case MODE_MAN_K:
- if (handle_whatis(ptr, use_mode) == 0)
- fprintf(stderr, "man: %s not found in whatis database\n", ptr);
- break;
- } /* End of switch(use_mode) */
- } /* End of else (parameter or title found) */
- argc -= 1;
- argv += 1;
- ptr = argv[0];
- }
-
- if (real_parameter_found == 0)
- option_error(0); /* no real parameters given during use */
-
- exit(0);
- } /* End of main() */
-
-
- /* Either no real parameter was given (just options: error == 0) */
- /* or options for different modes were combined (error == 1). */
- static void volatile option_error(const int error)
- {
- if (error == 1)
- fprintf(stderr, "man: options for different modes of operation cannot be combined\n");
- fprintf(stderr, "usage: man [-] [-M path] [-T macro-package] [[section] title ...] ...\n");
- fprintf(stderr, " man [-M path] -k keyword ...\n");
- fprintf(stderr, " man [-M path] -f command ...\n");
- exit(1);
- } /* End of option_exit() */
-
-
- /* Search all man directories until ready or end of path */
- static int search_man_path(const char *section, const char *title)
- {
- char this_path[NAME_LENGTH], *path_ptr;
- int title_done;
-
- title_done = 0;
- path_ptr = man_path;
- while ((*path_ptr != 0x00) && (title_done == 0))
- {
- int done;
- char *ptr;
-
- strcpy(this_path, path_ptr);
- ptr = this_path;
- /* Find first path separator or end of path */
- done = 0;
- while (!done)
- {
- if ((*path_ptr == 0x00) ||
- (*path_ptr == ';') ||
- (*path_ptr == ','))
- done = 1;
- else
- {
- path_ptr += 1;
- ptr += 1;
- }
- }
- if (*path_ptr != 0x00)
- path_ptr += 1;
- *ptr = 0x00;
-
- /* We now have a properly terminated search path in this_path */
- /* while path_ptr points to the rest of the manual search path. */
- title_done = search_man_dir(section, title, this_path);
- } /* End of while (not end of manual search path && title not done) */
- return(title_done);
- } /* End of search_man_path */
-
-
- /* Search a directory containing man?, cat? directories */
- static int search_man_dir(const char *section, const char *title,
- const char *dir_name)
- {
- int done;
- char section_counter;
-
- if (check_directory_name(dir_name) == 0)
- {
- fprintf(stderr, "man: directory %s on search path not found\n",
- dir_name);
- return(0);
- }
-
- if (section != NULL)
- {
- /* check whether a general or specific section is named */
- if (section[1] == 0x00)
- return(search_section_general(section[0], title, dir_name));
- else
- return(search_section_specific(section, title, dir_name));
- }
-
- for (section_counter = '1', done = 0;
- section_counter <= '8' && done == 0;
- section_counter++)
- {
- done = search_section_general(section_counter, title, dir_name);
- }
- return(done);
- } /* End of search_man_dir() */
-
-
- /* Search a section directory of the man dir for a specific man page */
- /* This isused when the section name is of the form number+code. */
- /* We only look for files with the name "dir/title.section" */
- static int search_section_specific(const char *section, const char *title,
- const char *dir_name)
- {
- char catdir_name[NAME_LENGTH],
- catfile_name[NAME_LENGTH],
- mandir_name[NAME_LENGTH],
- manfile_name[NAME_LENGTH],
- basename[NAME_LENGTH];
- int catdir_exists, catfile_exists, manfile_exists;
- struct stat catfile_stat, manfile_stat;
-
- strcpy(basename, title);
- strcat(basename, ".");
- strcat(basename, section);
-
- build_catdir_name(dir_name, section[0], catdir_name);
- catdir_exists = check_directory_name(catdir_name);
- strcpy(catfile_name, catdir_name);
- strcat(catfile_name, "/");
- strcat(catfile_name, basename);
- catfile_exists = !stat(catfile_name, &catfile_stat);
-
- build_mandir_name(dir_name, section[0], mandir_name);
- strcpy(manfile_name, mandir_name);
- strcat(manfile_name, "/");
- strcat(manfile_name, basename);
- manfile_exists = !stat(manfile_name, &manfile_stat);
-
- if (catfile_exists == 1)
- {
- if (manfile_exists == 0)
- {
- /* Only the catfile exists */
- display_file(catfile_name);
- return(1);
- }
- else if (catfile_stat.st_mtime > manfile_stat.st_mtime)
- {
- /* Both files exist, catfile is up-to-date */
- display_file(catfile_name);
- return(1);
- }
- else
- {
- /* Both file exist, catfile is not up-of-date */
- format_file(mandir_name, basename, catfile_name);
- display_file(catfile_name);
- return(1);
- }
- }
- else /* catfile does not exist */
- {
- if (manfile_exists == 0)
- return(0);
-
- if (catdir_exists == 1)
- {
- /* Make a formatted version of the manfile in the catdir, */
- /* then display the formatted manpage. */
- format_file(mandir_name, basename, catfile_name);
- display_file(catfile_name);
- return(1);
- }
- else
- {
- /* Make a formatted version of the manfile in a temporary file, */
- /* then display the temporary file. */
- tmpnam(catfile_name);
- format_file(mandir_name, basename, catfile_name);
- display_file(catfile_name);
- unlink(catfile_name);
- return(1);
- }
- } /* End of else (catfile doesn't exist) */
- return(0); /* This statement shouldn't be reached - better be safe */
- } /* End of search_section_specific() */
-
-
- /* Search a manual section looking for the best matching manual page */
- static int search_section_general(const char section_code, const char *title,
- const char *dir_name)
- {
- char catdir_name[NAME_LENGTH], mandir_name[NAME_LENGTH];
- char catfile_basename[NAME_LENGTH], catfile_fullname[NAME_LENGTH];
- char manfile_basename[NAME_LENGTH], manfile_fullname[NAME_LENGTH];
- int catdir_exists, mandir_exists;
- int catfile_type, manfile_type;
-
- build_catdir_name(dir_name, section_code, catdir_name);
- catdir_exists = check_directory_name(catdir_name);
- build_mandir_name(dir_name, section_code, mandir_name);
- mandir_exists = check_directory_name(mandir_name);
-
- if (catdir_exists == 1)
- catfile_type = find_best_file(catdir_name, title, section_code,
- catfile_basename, catfile_fullname);
- else
- catfile_type = 0; /* indicates no catfile found */
- if (mandir_exists == 1)
- manfile_type = find_best_file(mandir_name, title, section_code,
- manfile_basename, manfile_fullname);
- else
- manfile_type = 0; /* indicates no nanfile found */
-
- if ((catfile_type == 0) && (manfile_type == 0)) /* no files found */
- return(0);
-
- if (catfile_type > manfile_type) /* better type catfile than manfile */
- {
- display_file(catfile_fullname);
- return(1);
- }
- else if (catfile_type < manfile_type) /* better manfile than catfile */
- {
- if (catdir_exists == 1)
- {
- /* result from format is new catfile */
- strcpy(catfile_fullname, catdir_name);
- strcat(catfile_fullname, "/");
- strcat(catfile_fullname, manfile_basename);
- }
- else
- {
- /* result from format is a temporary file */
- tmpnam(catfile_fullname);
- }
- format_file(mandir_name, manfile_basename, catfile_fullname);
- display_file(catfile_fullname);
- if (catdir_exists == 0)
- unlink(catfile_fullname);
- return(1);
- } /* End of else (better manfile than catfile) */
- else /* catfile and manfile same quality */
- {
- if (strcmp(catfile_basename, manfile_basename) == 0)
- {
- /* Files have the same name - check if catfile up-to-date */
- struct stat catfile_stat, manfile_stat;
-
- stat(catfile_fullname, &catfile_stat);
- stat(manfile_fullname, &manfile_stat);
- if (catfile_stat.st_mtime > manfile_stat.st_mtime)
- {
- /* catfile is up-to-date */
- display_file(catfile_fullname);
- return(1);
- }
- else
- {
- /* manfile is newer than catfile */
- format_file(mandir_name, manfile_basename, catfile_fullname);
- display_file(catfile_fullname);
- return(1);
- }
- } /* end of if (same basename) */
- else
- {
- /* differing catfile and manfile - always take the catfile */
- display_file(catfile_fullname);
- return(1);
- }
- }
- return(0); /* should never be reached - better be safe */
- } /* End of search_section_general() */
-
-
- /* Find the best matching file in a directory */
- static int find_best_file(const char *dir_name, const char *title,
- const char section_code,
- char *result_basename, char *result_fullname)
- {
- /* Note on the return codes: higher number is better */
- /* code 5: short section extension (.3 in xxxxcat3) */
- /* code 4: extension .man */
- /* code 3: extension .nr */
- /* code 2: long section extension (.3x in xxxxcat3) */
- /* code 1: any other extension (first found is kept) */
- /* code 0: no file found at all */
- int return_code;
- DIR *search_dir;
- struct dirent *this_entry;
- char name_prefix[NAME_LENGTH];
- size_t prefix_size;
-
- return_code = 0;
- strcpy(name_prefix, title);
- strcat(name_prefix, ".");
- prefix_size = strlen(name_prefix);
- if ((search_dir = opendir(dir_name)) == (DIR *)NULL)
- return(0);
- while ((this_entry = readdir(search_dir)) != (struct dirent *)NULL)
- {
- if (strncmp(name_prefix, this_entry->d_name, prefix_size) == 0)
- {
- /* prefix matches - now check for type */
- int this_type = 0;
- char *extension;
-
- if (strlen(this_entry->d_name) > prefix_size)
- {
- extension = &(this_entry->d_name[prefix_size]);
- if ((extension[0] == section_code) &&
- (extension[1] == 0x00))
- this_type = 5;
- else if (strcmp(extension, "man") == 0)
- this_type = 4;
- else if (strcmp(extension, "nr") == 0)
- this_type = 3;
- else if ((extension[0] == section_code) &&
- /*(extension[1] != 0x00) && - we know this already */
- (extension[2] == 0x00))
- this_type = 2;
- else
- this_type = 1;
- }
- if (this_type > return_code)
- {
- return_code = this_type;
- strcpy(result_basename, this_entry->d_name);
- }
- } /* end of if (prefix matches) */
- } /* end of while (not end of directory) */
- closedir(search_dir);
- if (return_code > 0)
- {
- /* build result_fullname */
- strcpy(result_fullname, dir_name);
- strcat(result_fullname, "/");
- strcat(result_fullname, result_basename);
- }
- return(return_code);
- } /* End of find_best_file() */
-
-
- /* Display a file. Use pager for tty, cat for file or pipe */
- static void display_file(const char *filename)
- {
- char command_line[NAME_LENGTH], dosname[NAME_LENGTH];
-
- #ifdef SUBPROGRAMS_GET_GEMDOS_NAMES
- _unx2dos(filename, dosname); /* Send valid GEMDOS name through system */
- #else
- strcpy(dosname, filename);
- #endif
- if ((isatty(1) == 1) && /* GEMDOS handle 1 is stdout */
- (use_pager == 1)) /* No option '-' given */
- {
- /* Use a pager to display on a terminal */
- strcpy(command_line, pager);
- strcat(command_line, " ");
- strcat(command_line, dosname);
- if (system(command_line) == 0) /* 0: successful execution */
- return;
-
- fprintf(stderr, "man: error executing pager - aborted (sorry)\n");
- exit(1);
- }
- else
- {
- /* Use cat for anything but a tty or when '-' option is given */
- strcpy(command_line, "cat ");
- strcat(command_line, dosname);
- if (system(command_line) == 0) /* 0: successful execution */
- return;
-
- fprintf(stderr, "man: error executing cat - aborted (sorry)\n");
- exit(1);
- }
- } /* End of display_file() */
-
-
- /* Format (nroff) a file and write the result to another file */
- static void format_file(const char *mandir, const char *manfile,
- const char *catfile)
- {
- char command_line[NAME_LENGTH], cwd[NAME_LENGTH];
- #ifdef SUBPROGRAMS_GROK_GEMDOS_NAMES
- char dosname[NAME_LENGTH];
- #endif
-
- getcwd(cwd, NAME_LENGTH);
- if (chdir(mandir) != 0)
- {
- /* Note that the existence of the directory has been checked before */
- fprintf(stderr, "man: change directory to %s failed\n", mandir);
- exit(1);
- }
- strcpy(command_line, "nroff -m");
- strcat(command_line, macro_package);
- strcat(command_line, " ");
- #ifdef SUBPROGRAMS_GROK_GEMDOS_NAMES
- _unx2dos(manfile, dosname); /* send nroff a proper GEMDOS name */
- strcat(command_line, dosname);
- #else
- strcat(command_line, manfile);
- #endif
- strcat(command_line, " >");
- #ifdef SUBPROGRAMS_GROK_GEMDOS_NAMES
- _unx2dos(catfile, dosname); /* redirect to a proper GEMDOS name */
- strcat(command_line, dosname);
- #else
- strcat(command_line, catfile);
- #endif
- fprintf(stderr, "Formatting file. Wait...");
- if (system(command_line) == 0) /* successful execution */
- {
- fprintf(stderr, "Done\n");
- return;
- }
- else
- {
- fprintf(stderr, "Aborted (sorry)\n");
- exit(1);
- }
-
- if (chdir(cwd) != 0)
- {
- fprintf(stderr, "man: cannot change back to directory %s\n", cwd);
- exit(1);
- }
- } /* End of format_file() */
-
-
- /* Check if a dirname is a valid directory */
- static int check_directory_name(const char *dirname)
- {
- struct stat dir_stat;
-
- if (stat(dirname, &dir_stat) == -1)
- return(0);
- else if (dir_stat.st_mode & S_IFDIR == 0)
- {
- fprintf(stderr, "man: %s is not a directory\n", dirname);
- return(0);
- }
- else
- return(1);
- } /* End of check_directory_name() */
-
-
- /* Build the name of cat dir #section_code within the man dir */
- static void build_catdir_name(const char *dirname, const char section_code,
- char *catdir_name)
- {
- static char extension[] = "/cat0";
-
- strcpy(catdir_name, dirname);
- extension[4] = section_code;
- strcat(catdir_name, extension);
- } /* End of build_catdir_name() */
-
-
- /* Build the name of man dir #section_code within the man dir */
- static void build_mandir_name(const char *dirname, const char section_code,
- char *mandir_name)
- {
- static char extension[] = "/man0";
-
- strcpy(mandir_name, dirname);
- extension[4] = section_code;
- strcat(mandir_name, extension);
- } /* End of build_mandir_name() */
-
-
- /* Handle the `man -k' (apropos) and `man -f' (whatis) options */
- static int handle_whatis(const char *name, const mode_type mode)
- {
- char *path_ptr, name_used[NAME_LENGTH];
- int did_print;
-
- /* `man -f' (whatis) needs to have the filename stripped first */
- if (mode == MODE_MAN_F)
- strip_filename(name, name_used);
- else
- strcpy(name_used, name);
-
- did_print = 0;
- /* walk along manual path and check whatis database */
- path_ptr = man_path;
- while (*path_ptr != 0x00)
- {
- char mandir[NAME_LENGTH], *manptr;
-
- /* Copy one directory from the manual search path into mandir */
- manptr = mandir;
- while ((*path_ptr != 0x00) && (*path_ptr != ';') &&
- (*path_ptr != ','))
- *manptr++ = *path_ptr++;
- *manptr = 0x00;
- if (*path_ptr != 0x00)
- path_ptr += 1;
-
- if (check_directory_name(mandir) == 0)
- {
- fprintf(stderr, "man: directory %s on search path not found\n",
- mandir);
- continue; /* go on to next part of manual search path */
- }
-
- did_print |= print_apropos(mandir, name_used);
- } /* End of while (walk along manual search path) */
- return(did_print);
- } /* End of handle_whatis() */
-
-
- /* Strip the leading path-name and extension components from a filename. */
- /* This is needed for the `man -f' (whatis) option, which is the same as */
- /* `man -k' (apropos), except the whatis version needs a stripped name. */
- static void strip_filename(const char *name, char *stripped_name)
- {
- char buffer[NAME_LENGTH], *ptr1, *ptr2;
-
- strcpy(buffer, name);
- ptr1 = ptr2 = buffer;
- while (*ptr2 != 0x00)
- {
- if ((*ptr2 == '/') || (*ptr2 == '\\'))
- ptr1 = ptr2 + 1;
- ptr2 += 1;
- }
- ptr2 = ptr1;
- while ((*ptr2 != 0x00) && (*ptr2 != '.'))
- ptr2 += 1;
- *ptr2 = 0x00;
- strcpy(stripped_name, ptr1);
- } /* End of strip_filename() */
-
-
- /* Handle the `man -k' (apropos) search within a specific directory. */
- /* This is similar to the `man -f' (whatis) search, which also makes */
- /* use of this routine. The whatis argument must be stripped first. */
- static int print_apropos(const char *mandir, const char *name)
- {
- FILE *whatis;
- char line_read[NAME_LENGTH], line_buffer[NAME_LENGTH],
- name_buffer[NAME_LENGTH];
- int did_print;
-
- strcpy(name_buffer, mandir);
- strcat(name_buffer, "/whatis");
- if ((whatis = fopen(name_buffer, "r")) == NULL)
- {
- fprintf(stderr, "%s: no such file\n", name_buffer);
- return(0);
- }
-
- /* No case-insensitive strstr exists. Improvise with all-lowercase. */
- strcpy(name_buffer, name);
- strlwr(name_buffer);
- did_print = 0;
- while (!feof(whatis))
- {
- fgets(line_read, NAME_LENGTH - 1, whatis);
- strcpy(line_buffer, line_read);
- strlwr(line_buffer);
- if (strstr(line_buffer, name_buffer) != NULL)
- {
- fputs(line_read, stdout);
- did_print = 1;
- }
- }
- fclose(whatis);
- return(did_print);
- } /* End of print_apropos() */
-